package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.ConstantCollectionHandler;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.TaskLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.exceptions.FlowInvalidExpressionValueTypeException;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.CustomEventHandler;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.MultiExecutionStartedProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.MultiExecutionCompletedProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ExpressionUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.MultiInstanceKeyEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.CompletionCondition;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowMultiInstanceTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.factory.NodeCompletionConditionFactory;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowMultiInstanceTaskTraceRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.Activity;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.SubProcess;
import org.flowable.bpmn.model.Task;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.common.api.delegate.event.FlowableEngineEventType;
import org.flowable.engine.common.api.delegate.event.FlowableEvent;
import org.flowable.engine.common.api.delegate.event.FlowableEventType;
import org.flowable.engine.common.impl.el.ExpressionManager;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.event.impl.FlowableMultiInstanceActivityEventImpl;
import org.flowable.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author lilh
 * @date 2019-09-20 09:18
 */
@Slf4j
@Component
public class MultiInstanceActivityStartedHandler implements CustomEventHandler {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private FlowMultiInstanceTaskTraceRepository flowMultiInstanceTaskTraceRepository;

    @Autowired
    private NodeCompletionConditionFactory nodeCompletionConditionFactory;


    private RepositoryService repositoryService;


    private TaskService taskService;


    @Lazy
    @Autowired(required = false)
    private List<MultiExecutionStartedProcessor> multiExecutionStartedProcessorList = new ArrayList<>();

    @Override
    public FlowableEventType getEventType() {
        return FlowableEngineEventType.MULTI_INSTANCE_ACTIVITY_STARTED;
    }

    @Override
    public void handle(FlowableEvent event) throws Exception {
        if (!(event instanceof FlowableMultiInstanceActivityEventImpl)) {
            logger.info("事件类型不符，不处理");
            return;
        }
        FlowableMultiInstanceActivityEventImpl multiInstanceActivityEvent = (FlowableMultiInstanceActivityEventImpl) event;
        insertToMultiInstanceTaskTrace(multiInstanceActivityEvent);
        DelegateExecution execution = multiInstanceActivityEvent.getExecution();
        if (execution instanceof ExecutionEntity){
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            executeMultiExecutionStartedProcessor(executionEntity);
        }
        logger.info("开始会签节点");
    }

    private void executeMultiExecutionStartedProcessor(ExecutionEntity executionEntity) throws Exception {
        for (MultiExecutionStartedProcessor multiExecutionStartedProcessor : multiExecutionStartedProcessorList) {
            try {
                multiExecutionStartedProcessor.process(executionEntity);
            } catch (Throwable e) {
                log.error("executeMultiExecutionStartedProcessor error:",e);
                throw e;
            }
        }
    }

    private void insertToMultiInstanceTaskTrace(FlowableMultiInstanceActivityEventImpl multiInstanceActivityEvent) {
        ExecutionEntity multiRootExecution = CommandContextUtil.getExecutionEntityManager().findById(multiInstanceActivityEvent.getExecutionId());

        // 会签节点开始时，要把通过数和不通过数归零
        multiRootExecution.setVariableLocal(MultiInstanceKeyEnum.NUMBER_OF_PASS_INSTANCES.getCode(), 0);
        multiRootExecution.setVariableLocal(MultiInstanceKeyEnum.NUMBER_OF_UNPASS_INSTANCES.getCode(), 0);


        FlowMultiInstanceTaskTraceVo entity = new FlowMultiInstanceTaskTraceVo();
        entity.setProcessInstanceId(multiInstanceActivityEvent.getProcessInstanceId());
        entity.setProcessDefinitionId(multiInstanceActivityEvent.getProcessDefinitionId());
        entity.setActivityId(multiInstanceActivityEvent.getActivityId());
        entity.setActivityName(multiInstanceActivityEvent.getActivityName());
        entity.setRootExecutionId(multiInstanceActivityEvent.getExecutionId());

        entity.setNumOfActiveInstances(0);
        entity.setNumOfCompletedInstances(0);
        entity.setNumOfInstances(0);
        entity.setNumOfHandler(0);
        if (Objects.nonNull(multiRootExecution)) {
            MultiInstanceActivityBehavior behavior = (MultiInstanceActivityBehavior) ((Activity) (multiRootExecution.getCurrentFlowElement())).getBehavior();
            int numOfInstances = resolveNumOfInstance(multiRootExecution);
            entity.setNumOfInstances(numOfInstances);
            entity.setNumOfActiveInstances(numOfInstances);
            entity.setNumOfCompletedInstances(0);
            resolveNumOfHandler(entity, behavior,multiRootExecution);
        }
        entity.setNumOfPassInstances(0);
        entity.setNumOfUnpassInstances(0);
        entity.setStatus(ProcessInstanceStatusEnum.NORMAL.getCode());
        entity.setLinkStatus(TaskLinkStatusEnum.TODO.getCode());
        flowMultiInstanceTaskTraceRepository.insertMultiInstanceTaskTrace(entity);
    }

    private void resolveNumOfHandler(FlowMultiInstanceTaskTraceVo entity, MultiInstanceActivityBehavior behavior,ExecutionEntity executionEntity) {
        String completionCondition = behavior.getCompletionCondition();
        if (StrUtil.isBlank(completionCondition)) {
            behavior.setCompletionCondition("${nrOfCompletedInstances/nrOfInstances>=1}");
            entity.setNumOfHandler(entity.getNumOfInstances());
            return;
        }


        taskService = SpringUtils.getBean(TaskService.class);
        repositoryService = SpringUtils.getBean(RepositoryService.class);
        //org.flowable.task.api.Task task=taskService.createTaskQuery().processInstanceId(executionEntity.getProcessInstanceId()).list().get(0);
        //String processDefinitionId = task.getProcessDefinitionId();
        String processDefinitionId = executionEntity.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);

        String nodeCode = executionEntity.getCurrentFlowElement().getId();

        List<CompletionCondition> completionConditionList = nodeCompletionConditionFactory.getCompletionConditons(processDefinition.getDeploymentId(),nodeCode);
        if (completionConditionList != null && completionConditionList.size() > 0){
            completionCondition = completionConditionList.get(0).getValue();
        }

        String index;
        if (completionCondition.contains(">=")) {
            //解析出后面的数字
            index = "=";
        } else {
            index = ">";
        }
        String digital = completionCondition.substring(completionCondition.indexOf(index) + 1, completionCondition.length() - 1);
        if (completionCondition.contains("/")) {
            double rightValue = Double.parseDouble(digital) * entity.getNumOfInstances();
            if (completionCondition.contains(">=")) {
                entity.setNumOfHandler((int) Math.ceil(rightValue));
            } else {
                entity.setNumOfHandler((int) Math.floor(rightValue + 1));
            }

        } else {
            Integer numOfInstances = entity.getNumOfInstances();
            if (completionCondition.contains(">=")) {
                entity.setNumOfHandler(Math.min(Integer.parseInt(digital),numOfInstances));
            } else {
                entity.setNumOfHandler(Math.min(Integer.parseInt(digital) + 1,numOfInstances));
            }
        }

    }

    private int resolveNumOfInstance(ExecutionEntity multiRootExecution) {

        FlowElement flowElement = multiRootExecution.getCurrentFlowElement();
        if (flowElement instanceof Activity){
            Activity activity = (Activity) flowElement;
            String collectionString = activity.getLoopCharacteristics().getCollectionString();
            if (activity instanceof SubProcess) {
                return resolveNumOfInstanceForSubProcess(multiRootExecution, activity);
            }

            ConstantCollectionHandler constantCollectionHandler = SpringUtils.getBean(ConstantCollectionHandler.class);
            Collection collection = constantCollectionHandler.resolveCollection(collectionString,multiRootExecution);
            if (Objects.nonNull(collection)){
                return collection.size();
            }
            return 0;

            /*String reg = "\\$\\{\\w+}";
            if (StringUtils.isNotBlank(collectionString)){
                if (collectionString.contains("${")) {
                    Pattern pattern = Pattern.compile(reg);
                    Matcher matcher = pattern.matcher(collectionString);
                    if (matcher.find()) {
                        String result = matcher.group();
                        result = result.replace("${", "");
                        result = result.replace("}", "");
                        Object obj = multiRootExecution.getVariable(result);
                        if (Objects.nonNull(obj)) {
                            if (obj instanceof List) {
                                List assignList = (List) obj;
                                return assignList.size();
                            }else if (obj instanceof String){
                                String candidateUserStr = String.valueOf(obj);
                                String[] candidateUsers = candidateUserStr.split(",");
                                return candidateUsers.length;
                            }
                        }
                    }
                }else{
                    JSONObject jsonObject = JSONObject.parseObject(collectionString);
                    Collection<Object> collection = jsonObject.values();
                    int i = 0;
                    if (Objects.nonNull(collection) && !collection.isEmpty()){
                        for (Object o : collection) {
                            if (o instanceof String){
                                String strValue = String.valueOf(o);
                                String[] assignee = strValue.split(",");
                                i += assignee.length;
                            }else if (o instanceof List){
                                List assignList = (List) o;
                                i += assignList.size();
                            }
                        }
                    }
                    return i;
                }
            }*/

        }
        return 0;
    }

    private int resolveNumOfInstanceForSubProcess(ExecutionEntity multiRootExecution, Activity activity) {
        Assert.isInstanceOf(SubProcess.class, activity);
        SubProcess subProcess = (SubProcess)activity;
        String collectionString = subProcess.getLoopCharacteristics().getInputDataItem();
        //子流程的collection string肯定是一个变量，形如${loopNum}
        String messagePrefix = String.format("流程实例 %s 中，子流程 %s 中的复制变量(%s)",
            multiRootExecution.getProcessInstanceId(), subProcess.getName(), collectionString);
        if (!ExpressionUtils.isExpression(collectionString)) {
            throw new FlowInvalidExpressionValueTypeException(
                String.format("%s设置不正确, 这个需要是一个变量格式，例如${loopNum}", messagePrefix));
        }

        ExpressionManager expressionManager = ExpressionUtils.getExpressionManager();
        Object expressValueObj = expressionManager.createExpression(collectionString).getValue(multiRootExecution);
        if (Objects.isNull(expressValueObj)){
            return 0;
        }

        if (!(expressValueObj instanceof List)) {
            throw new FlowInvalidExpressionValueTypeException(
                String.format("流程实例 %s 中，子流程 %s 中的复制变量(%s)的值，应该是一个List，例如：[\"123\", \"345\"]，现在是：%s",
                    messagePrefix,
                    JSONObject.toJSONString(expressValueObj, true)));
        }

        return ((List)expressValueObj).size();

    }
}
